//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "aura.h"
#include "wince.h"
#include "private.h"

typedef struct _ThreadArg {
    PAuraThreadMain m_pUserEntry;
    Aura_PVoid m_pUserArgs;
    ThreadDescr * m_pThreadDescr;
    BOOL m_bAutoClose;
}ThreadArgument;

// for "Thread"

AURA_API_(Aura_ThreadId) AuraThread_GetCurrentId()
{
    return GetCurrentThreadId();
}

AURA_API_(void) AuraThread_Quit(
        /* [in] */ Aura_QuitCode quitCode)
{
    ExitThread((DWORD)quitCode);

    assert(0);
    return ;
}

STATIC DWORD WINAPI _ThreadMainRoutine(LPVOID pArgs)
{
    ThreadArgument * pArgument = (ThreadArgument *)pArgs;
    PAuraThreadMain pUserEntry = pArgument->m_pUserEntry;
    Aura_PVoid pUserArgs = pArgument->m_pUserArgs;
    ThreadDescr * pThreadDescr = pArgument->m_pThreadDescr;
    BOOL bAutoClose = pArgument->m_bAutoClose;

    // BUG: make sure success for setting tls value.
    TlsSetValue(s_threadDescrIndex, (LPVOID)pThreadDescr);

    Aura_free((void *)pArgument);
    pUserEntry(pUserArgs);

    // just for few services thread.
    if (bAutoClose) {
        CloseHandle(pThreadDescr->m_hEvent);
        CloseHandle(pThreadDescr->m_hThread);
        Aura_free((void *)pThreadDescr);
    }
    // assert(0);
    return 0;
}

AURA_API AuraThread_Create(
        /* [in] */ PAuraThreadMain pEntry,
        /* [in] */ Aura_PVoid pArgument,
        /* [in] */ Aura_ThreadCreationFlags creationFlags,
        /* [out] */ Aura_Handle * phThread,
        /* [out] */ Aura_ThreadId * pThreadId)
{
    ThreadArgument * pThreadArgs = NULL;
    ThreadDescr * pThreadDescr = NULL;
    HANDLE hThread = NULL;
    DWORD dwCreateFlags = 0;
    DWORD  threadId = 0;
    HANDLE hEvent = NULL;
    Aura_ECode ec = AURA_NOERROR;

    pThreadArgs = (ThreadArgument *)Aura_malloc(sizeof(ThreadArgument));
    if (NULL == pThreadArgs) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    memset((void *)pThreadArgs, 0, sizeof(ThreadArgument));
    pThreadArgs->m_pUserEntry = pEntry;
    pThreadArgs->m_pUserArgs  = pArgument;
    if (NULL == phThread) {
        pThreadArgs->m_bAutoClose = TRUE;
    }

    hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    if (NULL == hEvent) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }

    pThreadDescr = (ThreadDescr *)Aura_malloc(sizeof(ThreadDescr));
    if (NULL == pThreadDescr) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    memset((void *)pThreadDescr, 0, sizeof(ThreadDescr));
    pThreadDescr->m_inWaitList.Initialize();
    pThreadDescr->m_eventState = AURA_UNSIGNALED;
    pThreadDescr->m_hEvent = hEvent;
    pThreadArgs->m_pThreadDescr = pThreadDescr;

    if (creationFlags & Aura_ThreadCreationFlag_Unstarted) {
        dwCreateFlags = CREATE_SUSPENDED;
    }

    hThread = CreateThread(NULL, (DWORD)0,
                        _ThreadMainRoutine,
                        (Aura_PVoid)pThreadArgs,
                        dwCreateFlags,
                        &threadId);
    if (NULL == hThread) {
        DWORD dwError = GetLastError();
        ec = (ERROR_NOT_ENOUGH_MEMORY == dwError)
                   ? AURA_E_NOT_ENOUGH_ADDRESS_SPACE
                     : Win32ErrorToECodeError(dwError);
        goto E_FAIL_EXIT;
    }

    pThreadDescr->m_hThread = hThread;
    if (NULL != phThread) {
        *phThread = (Aura_Handle)pThreadDescr;
    }

    if (NULL != pThreadId) {
        *pThreadId = threadId;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    if (NULL != pThreadArgs) {
        Aura_free((void *)pThreadArgs);
        pThreadArgs = NULL;
    }
    if (NULL != hEvent) {
        CloseHandle(hEvent);
        hEvent = NULL;
    }
    if (NULL != pThreadDescr) {
        Aura_free((void *)pThreadDescr);
        pThreadDescr = NULL;
    }
    return ec;
}

AURA_API AuraThread_Open(
        /* [in] */ Aura_ThreadId threadId,
        /* [out] */ Aura_Handle * phThread)
{
    ThreadDescr * pThreadDescr = NULL;
    Aura_Handle hThread = NULL;
    Aura_Handle hEvent  = NULL;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != phThread);

    pThreadDescr = (ThreadDescr *)Aura_malloc(sizeof(ThreadDescr));
    if (NULL == pThreadDescr) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    memset((void *)pThreadDescr, 0, sizeof(ThreadDescr));
    pThreadDescr->m_inWaitList.Initialize();
    pThreadDescr->m_eventState = AURA_UNSIGNALED;

    hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    if (NULL == hEvent) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    pThreadDescr->m_hEvent = hEvent;
    pThreadDescr->m_hThread = GetCurrentThread();

    //BUG: make sure the tls's setting be succeed.
    TlsSetValue(s_threadDescrIndex, (LPVOID)pThreadDescr);

    *phThread = (Aura_Handle)pThreadDescr;
    return AURA_NOERROR;

E_FAIL_EXIT:
    if (NULL != pThreadDescr) {
        Aura_free((void *)pThreadDescr);
    }
    if (NULL != hEvent) {
        CloseHandle(hEvent);
    }
    return ec;
}

AURA_API AuraThread_Resume(
        /* [in] */ Aura_Handle hThread)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    DWORD dwRes = 0;
    assert(NULL != pThreadDescr);

    dwRes = ResumeThread(pThreadDescr->m_hThread);
    if (((DWORD)-1) == dwRes) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_Suspend(
        /* [in] */ Aura_Handle hThread)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    DWORD dwRes = 0;
    assert(NULL != pThreadDescr);

    dwRes = SuspendThread(pThreadDescr->m_hThread);
    if (((DWORD)-1) == dwRes) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_Join(
        /* [in] */ Aura_Handle hThread,
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    DWORD hRes = 0;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != pThreadDescr);
    assert(NULL != pResult);

    hRes = WaitForSingleObject(pThreadDescr->m_hThread, (DWORD)timeOut);
    switch(hRes) {
    case WAIT_OBJECT_0:
        *pResult = Aura_WaitResult_OK;
        break;
    case WAIT_TIMEOUT:
        *pResult = Aura_WaitResult_TimedOut;
        break;
    case WAIT_ABANDONED:
    default:
        ec = GetLastECodeError();
        break;
    }

    return ec;
}

AURA_API AuraThread_Abort(
        /* [in] */ Aura_Handle hThread,
        /* [in] */ Aura_QuitCode quitCode)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    BOOL bSuccess = FALSE;

    assert(NULL != pThreadDescr);
    bSuccess = TerminateThread(pThreadDescr->m_hThread, quitCode);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_Interrupt(
        /* [in] */ Aura_Handle hThread)
{
    return AURA_NOERROR;
}

AURA_API AuraThread_Destroy(
        /* [in] */ Aura_Handle hThread)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    BOOL bSuccess = FALSE;
    assert(NULL != pThreadDescr);

    bSuccess = CloseHandle((HANDLE)pThreadDescr->m_hThread);
    Aura_free((void *)pThreadDescr);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_GetPriority(
        /* [in] */ Aura_Handle hThread,
        /* [out] */ Aura_ThreadPriority * pPriority)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    INT priority;

    assert(NULL != pThreadDescr);
    assert(NULL != pPriority);

    priority = GetThreadPriority(pThreadDescr->m_hThread);
    if (THREAD_PRIORITY_ERROR_RETURN == priority) {
        goto E_FAIL_EXIT;
    }

    *pPriority = WinPriorityConvertToAura(priority);
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_SetPriority(
        /* [in] */ Aura_Handle hThread,
        /* [in] */ Aura_ThreadPriority priority)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    INT tmpPriority = 0;
    BOOL bSuccess = FALSE;

    assert(NULL != pThreadDescr);

    tmpPriority = AuraPriorityConvertToWin(priority);
    bSuccess = SetThreadPriority(pThreadDescr->m_hThread, tmpPriority);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_GetLifespan(
        /* [in] */ Aura_Handle hThread,
        /* [out] */ Aura_SystemTime * pStartTime,
        /* [out] */ Aura_SystemTime * pQuitTime)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    BOOL bSuccess = FALSE;
    FILETIME time[2];

    assert(NULL != pThreadDescr);
    assert((NULL != pStartTime) || (NULL != pQuitTime));

    bSuccess = GetThreadTimes(pThreadDescr->m_hThread,
                         &time[0], &time[1],
                         NULL, NULL);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    if (NULL != pStartTime) {
        WinFileTimeConvertToAuraSystime(&time[0],pStartTime);
    }
    if (NULL != pQuitTime) {
        WinFileTimeConvertToAuraSystime(&time[1],pQuitTime);
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_GetProcessorTimes(
        /* [in] */ Aura_Handle hThread,
        /* [out] */ Aura_Millisecond32 * pUserTime,
        /* [out] */ Aura_Millisecond32 * pPrivilegedTime)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    BOOL bSuccess = FALSE;
    FILETIME time[4];

    assert(NULL != pThreadDescr);
    assert((NULL != pUserTime) || (NULL != pPrivilegedTime));

    bSuccess = GetThreadTimes(pThreadDescr->m_hThread,
                           &time[0], &time[1],
                           &time[2], &time[3]);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    if (NULL != pUserTime) {
        WinFileTimeConvertToMilliseconds(&time[3], pUserTime);
    }
    if (NULL != pPrivilegedTime) {
        WinFileTimeConvertToMilliseconds(&time[2], pPrivilegedTime);
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_GetQuitCode(
        /* [in] */ Aura_Handle hThread,
        /* [out] */ Aura_QuitCode * pQuitCode)
{
    ThreadDescr * pThreadDescr = (ThreadDescr *)hThread;
    DWORD dwExitCode;
    BOOL bSuccess = FALSE;

    assert(NULL != pThreadDescr);
    assert(NULL != pQuitCode);

    bSuccess = GetExitCodeThread(pThreadDescr->m_hThread, &dwExitCode);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }

    *pQuitCode = (Aura_QuitCode)dwExitCode;
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraThread_Sleep(
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    DWORD dwRes = 0;
    assert(NULL != pResult);

    Sleep((DWORD)timeOut);
    *pResult = Aura_WaitResult_TimedOut;

    return AURA_NOERROR;
}

AURA_API AuraThread_Yield()
{
    Sleep(0);
    return AURA_NOERROR;
}


// for "TLS"

AURA_API AuraTls_Allocate(
    /* [out] */ Aura_UInt32 * pIndex)
{
    DWORD dwTlsIndex;

    assert(NULL != pIndex);

    dwTlsIndex = TlsAlloc();
    if (((DWORD)-1) == dwTlsIndex) {
        *pIndex = (DWORD)-1;
        return GetLastECodeError();
    }

    *pIndex = (Aura_UInt32)dwTlsIndex;
    return AURA_NOERROR;
}

AURA_API AuraTls_Free(
    /* [in]  */ Aura_UInt32 uIndex)
{
    BOOL bSuccess = FALSE;

    bSuccess = TlsFree((DWORD)uIndex);
    if (!bSuccess) {
        return GetLastECodeError();
    }
    return AURA_NOERROR;
}

AURA_API AuraTls_GetValue(
    /* [in]  */ Aura_UInt32 uIndex,
    /* [out] */ Aura_PVoid * ppValue)
{
    LPVOID pTlsValue = NULL;

    assert(NULL != ppValue);

    pTlsValue = TlsGetValue((DWORD)uIndex);
    if (NULL == pTlsValue) {
        return GetLastECodeError();
    }

    *ppValue = pTlsValue;
    return AURA_NOERROR;
}

AURA_API AuraTls_SetValue(
    /* [in]  */ Aura_UInt32 uIndex,
    /* [in]  */ Aura_PVoid pValue)
{
    BOOL bSuccess = FALSE;

    bSuccess = TlsSetValue((DWORD)uIndex, (LPVOID)pValue);
    if (!bSuccess) {
        return GetLastECodeError();
    }
    return AURA_NOERROR;
}

